﻿
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using sharpxbot.Core.Plugins;
using sharpxbot.Plugins.Base;
using sharpxbot.Plugins.Context;
using sharpxbot.Utils.IO;
namespace sharpxbot.Plugins.Loaders
{
    public class PluginLoader : IPluginLoader
    {
        public PluginLoader(IPluginContext context, IAdminContext adminContext)
        {
            if (context == null || adminContext == null)
                throw new NullReferenceException("Illegal null-reference plugin context or admin context. PluginLoader ctor");

            this.context = context;
            this.adminContext = adminContext;
        }

        #region IPluginLoader Members

        public IPlugin[] LoadPlugins(string path)
        {
            List<IPlugin> result = new List<IPlugin>();

            try
            {
                FileUtil util = new FileUtil();
                var pluginFiles = util.GetFiles(path, "*.dll");
                var pluginTypes = new List<Type>();

                foreach (FileInfo f in pluginFiles)
                {
                    pluginTypes.AddRange(TryLoadPluginTypes(f));
                }

                result.AddRange(CreatePlugins(pluginTypes));
                result.AddRange(LoadBuiltInPlugins());
            }
            catch { }

            return result.ToArray();
        }

        private IEnumerable<Type> TryLoadPluginTypes(FileInfo f)
        {
            List<Type> result = new List<Type>();

            try
            {
                Assembly pluginAssembly = Assembly.LoadFrom(f.FullName);
                if (pluginAssembly != null)
                {
                    var avalibaleTypes = pluginAssembly.GetTypes();
                    foreach (Type t in avalibaleTypes)
                    {
                        if (typeof(IPlugin).IsAssignableFrom(t))
                        {
                            result.Add(t);
                        }
                    }
                }
            }
            catch { }

            return result;
        }

        private IEnumerable<IPlugin> CreatePlugins(IEnumerable<Type> types)
        {
            List<IPlugin> result = new List<IPlugin>();

            foreach (Type t in types)
            {
                try
                {
                    var constructors = t.GetConstructors();

                    ConstructorInfo defaultConstructor = null;
                    foreach (ConstructorInfo c in constructors)
                    {
                        if (c.GetParameters().Length == 0)
                        {
                            defaultConstructor = c;
                            break;
                        }
                    }

                    if (defaultConstructor != null)
                    {
                        result.Add((IPlugin)defaultConstructor.Invoke(null));
                    }
                    else
                    {
                        ConstructorInfo abstractPluginConstructor = null;
                        foreach (ConstructorInfo c in constructors)
                        {
                            ParameterInfo[] ctorParams = c.GetParameters();
                            if (ctorParams.Length == 1)
                            {
                                if (typeof(IPluginContext).IsAssignableFrom(ctorParams[0].ParameterType))
                                {
                                    abstractPluginConstructor = c;
                                    break;
                                }
                            }
                        }

                        if (abstractPluginConstructor != null)
                        {
                            result.Add(
                                (IPlugin)abstractPluginConstructor.Invoke(new object[] { context }));
                        }
                    }
                }
                catch { }
            }

            return result;
        }

        private IEnumerable<IPlugin> LoadBuiltInPlugins()
        {
            List<IPlugin> plugins = new List<IPlugin>();

            plugins.Add(new TrafficLimiterPlugin(context, adminContext));
            plugins.Add(new NickSpamFilterPlugin(context));
            plugins.Add(new SystemInfoPlugin(context));
            plugins.Add(new MucAdminPlugin(context));
            plugins.Add(new GroupChatRulesPlugin(context));
            plugins.Add(new WebLinksPlugin(context));
            //TODO add plugins here

            return plugins;
        }

        #endregion

        private IPluginContext context;
        private IAdminContext adminContext;
    }
}
